package com.base.data.elasticsearch.core;

import com.google.gson.JsonObject;
import com.base.data.elasticsearch.ElasticsearchException;
import com.base.data.elasticsearch.util.GsonUtil;
import com.base.data.elasticsearch.util.MiddleLevelRestClient;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.client.*;
import org.elasticsearch.common.CheckedFunction;
import org.elasticsearch.common.xcontent.XContentParser;

import java.io.IOException;
import java.util.Set;


public class BaseRestHighLevelClient extends RestHighLevelClient {

    private MiddleLevelRestClient middleLevelRestClient;

    public BaseRestHighLevelClient(RestClient restClient, MiddleLevelRestClient middleLevelRestClient) {
        super(restClient);
        this.middleLevelRestClient = middleLevelRestClient;
    }

    @Override
    protected <Req extends ActionRequest, Resp> Resp performRequestAndParseEntity(Req request, CheckedFunction<Req, Request, IOException> requestConverter, CheckedFunction<XContentParser, Resp, IOException> entityParser, Set<Integer> ignores, Header... headers) throws IOException {
        return super.performRequestAndParseEntity(request, requestConverter, entityParser, ignores, headers);
    }

    /**
     *  使用middleLevelRestClient 修改 配置参数
     * @param request
     * @param requestConverter
     * @param responseConverter
     * @param ignores
     * @param headers
     * @param <Req>
     * @param <Resp>
     * @return
     * @throws IOException
     */
    @Override
    protected <Req extends ActionRequest, Resp> Resp performRequest(Req request, CheckedFunction<Req, Request, IOException> requestConverter, CheckedFunction<Response, Resp, IOException> responseConverter, Set<Integer> ignores, Header... headers) throws IOException {
        ActionRequestValidationException validationException = request.validate();
        if (validationException != null) {
            throw validationException;
        }
        Request req = requestConverter.apply(request);
        Response response;
        try {
            //restClient -> middleLevelRestClient
            response = middleLevelRestClient.performRequest(req.getMethod(), req.getEndpoint(), req.getParameters(), req.getEntity(), headers);
        } catch (ResponseException e) {
            if (ignores.contains(e.getResponse().getStatusLine().getStatusCode())) {
                try {
                    return responseConverter.apply(e.getResponse());
                } catch (Exception innerException) {
                    //the exception is ignored as we now try to parse the response as an error.
                    //this covers cases like get where 404 can either be a valid document not found response,
                    //or an error for which parsing is completely different. We try to consider the 404 response as a valid one
                    //first. If parsing of the response breaks, we fall back to parsing it as an error.
                    throw parseResponseException(e);
                }
            }
            throw parseResponseException(e);
        }

        try {
            return responseConverter.apply(response);
        } catch(Exception e) {
            throw new IOException("Unable to parse response body for " + response, e);
        }
    }

    @Override
    protected <Req extends ActionRequest, Resp> void performRequestAsyncAndParseEntity(Req request, CheckedFunction<Req, Request, IOException> requestConverter, CheckedFunction<XContentParser, Resp, IOException> entityParser, ActionListener<Resp> listener, Set<Integer> ignores, Header... headers) {
        super.performRequestAsyncAndParseEntity(request, requestConverter, entityParser, listener, ignores, headers);
    }

    @Override
    protected <Req extends ActionRequest, Resp> void performRequestAsync(Req request, CheckedFunction<Req, Request, IOException> requestConverter, CheckedFunction<Response, Resp, IOException> responseConverter, ActionListener<Resp> listener, Set<Integer> ignores, Header... headers) {
        ActionRequestValidationException validationException = request.validate();
        if (validationException != null) {
            listener.onFailure(validationException);
            return;
        }
        Request req;
        try {
            req = requestConverter.apply(request);
        } catch (Exception e) {
            listener.onFailure(e);
            return;
        }

        ResponseListener responseListener = wrapResponseListener(responseConverter, listener, ignores);
        //restClient -> middleLevelRestClient
        middleLevelRestClient.performRequestAsync(req.getMethod(), req.getEndpoint(), req.getParameters(), req.getEntity(), responseListener, headers);
    }

    <Resp> ResponseListener wrapResponseListener(CheckedFunction<Response, Resp, IOException> responseConverter,
                                                 ActionListener<Resp> actionListener, Set<Integer> ignores) {
        return new ResponseListener() {
            @Override
            public void onSuccess(Response response) {
                try {
                    actionListener.onResponse(responseConverter.apply(response));
                } catch(Exception e) {
                    IOException ioe = new IOException("Unable to parse response body for " + response, e);
                    onFailure(ioe);
                }
            }

            @Override
            public void onFailure(Exception exception) {
                if (exception instanceof ResponseException) {
                    ResponseException responseException = (ResponseException) exception;
                    Response response = responseException.getResponse();
                    if (ignores.contains(response.getStatusLine().getStatusCode())) {
                        try {
                            actionListener.onResponse(responseConverter.apply(response));
                        } catch (Exception innerException) {
                            //the exception is ignored as we now try to parse the response as an error.
                            //this covers cases like get where 404 can either be a valid document not found response,
                            //or an error for which parsing is completely different. We try to consider the 404 response as a valid one
                            //first. If parsing of the response breaks, we fall back to parsing it as an error.
                            actionListener.onFailure(parseResponseException(responseException));
                        }
                    } else {
                        actionListener.onFailure(parseResponseException(responseException));
                    }
                } else {
                    actionListener.onFailure(exception);
                }
            }
        };
    }

    // 新增的方法
    static boolean convertCreateIndexResponse(Response response) {
        return response.getStatusLine().getStatusCode() == 200;
    }

    static boolean convertIndicesExistsResponse(Response response) {
        return response.getStatusLine().getStatusCode() == 200 && "ok".equalsIgnoreCase(response.getStatusLine().getReasonPhrase());
    }

    static JsonObject convertTypesExistsResponse(Response response) {
        if(response.getStatusLine().getStatusCode() == 200)  {
            HttpEntity httpEntity = response.getEntity();
            String text = null;
            try {
                text = IOUtils.toString(httpEntity.getContent(),"UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
            }
            return GsonUtil.parse(text);
        }
        throw new ElasticsearchException("convertTypesExistsResponse err");
    }



}
